package com.liangwj.springbootquickstart.rpc;

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Filename: ServerCenter.java
 * @Description:  服务端实现类
 * @Version: 1.0
 * @Author: Wenjun.Liang
 * @Email: Wenjun.Liang@paat.com
 * @History:<br>
 * <li>Author: Wenjun.Liang </li>
 * <li>Date: 2019/6/18-14:13 </li>
 * <li>Version: 1.0 </li>
 * <li>Content: create </li>
 */
public class ServerCenter implements Server{

    /**
     * 设置线程名称
     */
    private static ThreadFactory nameThreadFactory = new ThreadFactoryBuilder().setNameFormat("rpc-pool-%d").build();
    /**
     * 线程池 接受客户端调用
     */
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 200, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(10), nameThreadFactory, new ThreadPoolExecutor.AbortPolicy());

    /**
     * 服务注册缓存
     */
    public static final Map<String, Class<?>> SERVICE_REGISTRY = new HashMap<>();

    @Override
    public void start() throws IOException {
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(PORT));
        try {
            while (true) {
                executor.execute(new ServiceTask(server.accept()));
            }
        } catch (Exception e) {
            server.close();
        }
    }

    @Override
    public void stop() {
        executor.shutdown();
    }

    @Override
    public void register(Class<? extends IRpcService> serviceInterface, Class<? extends IRpcService> impl) {
        System.out.println("注册服务： serviceName:" +serviceInterface.getName() );
        SERVICE_REGISTRY.put(serviceInterface.getName(), impl);
    }

    private static class ServiceTask implements Runnable {

        Socket client = null;

        public ServiceTask(Socket client) {
            this.client = client;
        }

        @Override
        public void run() {
            ObjectInputStream input = null;
            ObjectOutputStream output = null;
            try {
                input = new ObjectInputStream(client.getInputStream());
                String serviceName = input.readUTF();
                String methodName = input.readUTF();

                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                Object[] arguments = (Object[]) input.readObject();
                Class<?> serviceClass = SERVICE_REGISTRY.get(serviceName);
                if (serviceClass == null) {
                    throw new ClassNotFoundException(serviceName + "not found");
                }
                Method method = serviceClass.getMethod(methodName, parameterTypes);
                Object result = method.invoke(serviceClass.newInstance(), arguments);

                // 将执行结果反序列化 通过socket返给客户端
                output = new ObjectOutputStream(client.getOutputStream());
                output.writeObject(result);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if(input != null){
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                if(output != null){
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                if(client != null){
                    try {
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) throws IOException {
        ServerCenter center = new ServerCenter();
        center.register(IHelloService.class, HelloServiceImpl.class);
        center.start();
    }

}
